<template>
  <div :class="['minAiContainer', size]" :style="containerStyle">
    <div class="minAiHeader" v-if="hasHeader">
      <div class="logoBox">
        <img src="@/assets/ai/logo.png" />
        <div class="title">{{ t('ai', 'AI小助手') }}</div>
      </div>
      <!-- <div class="titleText">AI 助手</div>
			<div class="closeBtn" @click="handleClose">×</div> -->
    </div>
    <div class="minAiBg" v-if="hasBg">
      <img src="@/assets/ai/聊天bg.png" />
    </div>
    <div class="messageContent" ref="messageContent">
      <div
        v-for="(msg, index) in newMessageList"
        :key="index"
        :class="['messageItem', msg.source]"
      >
        <!-- <div class="avatarBox">
					<img :src="msg.source === 'ai' ? aiAvatar : userAvatar" @error="() => console.log(111)">
				</div> -->
        <div class="textBox">
          <div
            class="textContent"
            v-html="renderMarkDown(msg.answer || '')"
          ></div>
          <div
            style="
              display: flex;
              margin-top: 10px;
              justify-content: space-between;
              align-items: center;
            "
            v-if="msg.source === 'ai' && currentEvents[index] == 'message_end'"
          >
            <CopyOutlined
              v-if="btns.includes('copy')"
              @click="handleCopy(msg.answer || '')"
              style="cursor: pointer; color: #b5acce"
            />
            <div class="textBtn" v-if="msg.answer?.includes('处理') && msg.url"
              >{{t('ai', '立即处理')}}</div
            >
          </div>
        </div>
      </div>
    </div>
    <div class="inputArea">
      <div class="filesBox">
        <div class="filesList">
          <a-tag
            :bordered="false"
            closable
            @close="handleFilesDelete(item, index)"
            v-for="(item, index) in files"
            style="display: flex; margin-bottom: 10px; width: 30%"
          >
            <div class="ellipsis" style="">{{ item.name }}</div>
          </a-tag>
        </div>
        <a-button v-if="files.length" @click="handleSend(`${t('ai', '我已完成任务')}`,{taskId: record.id})">{{ t('ai', '已全部上传') }}</a-button>
      </div>
      <!-- :disabled="isLoading"
      :style="{ cursor: isLoading ? 'not-allowed' : 'pointer' }" -->
      <textarea
        v-model="inputMessage"
        ref="textarea"
        :placeholder="initialData.placeholder || t('common', '请输入')"
        :rows="rows"
        @keydown="handleKeyDown"
      ></textarea>
      <div class="actionBtns">
        <div
          class="btn"
          style="color: #333"
          v-if="isLoading"
          :style="{ cursor: isLoading ? 'not-allowed' : 'pointer' }"
        >
          <LoadingOutlined />
        </div>
        <minFile
          :accept="accept"
          :maxSize="maxSize"
          @handleReadFile="handleReadFile"
          :disabled="isLoading"
          v-if="btns.includes('file') && record.id"
        >
          <div
            class="btn"
            style="color: #8985b1"
            :style="{ cursor: isLoading ? 'not-allowed' : 'pointer' }"
          >
            <LoadingOutlined v-if="uploadLoading" />
            <PaperClipOutlined v-else />
          </div>
        </minFile>
        <div v-if="!isLoading">
          <div
            class="btn primary"
            v-if="!isLoading && inputMessage"
            @click="handleSend"
          >
            <SendOutlined />
          </div>
          <div class="btn" v-else style="color: #8985b1; cursor: default">
            <SendOutlined />
          </div>
        </div>
        <div
          class="btn primary"
          v-if="isLoading && btns.includes('stop')"
          @click="handleStop"
        >
          <PauseOutlined />
        </div>
        <!-- <div>
          <a-button
            style="margin-right: 8px"
            v-if="btns.includes('audio')"
            :size="size"
            shape="circle"
            :icon="h(AudioOutlined)"
            @click="handleAudio"
          />
          <a-button
            style="margin-right: 8px"
            v-if="isLoading && btns.includes('stop')"
            :size="size"
            shape="circle"
            :icon="h(PauseOutlined)"
            @click="handleStop"
          />
          <a-button
            :loading="isLoading"
            :size="size"
            type="primary"
            shape="circle"
            :icon="h(SendOutlined)"
            @click="handleSend"
          />
        </div> -->
      </div>
    </div>
  </div>
</template>

<script setup>
import { t } from '@/i18n'
import {
  ref,
  reactive,
  nextTick,
  onMounted,
  watch,
  h,
  computed,
  onUnmounted,
  defineExpose
} from 'vue'
import {
  SendOutlined,
  PauseOutlined,
  LoadingOutlined,
  CopyOutlined,
  PaperClipOutlined,
  AudioOutlined,
} from '@ant-design/icons-vue'
import minFile from '@/components/common/minFile.vue'
import { message } from 'ant-design-vue'
import MarkdownIt from 'markdown-it'
// import { sendMessageToAgent, textGenerateAudio, audioGenerateText } from '@/request/request'
import { sendMessageToAgent, learningTaskSubmitTask, learningTaskDelFile } from '@/request/ai'

const {
  accept,
  maxSize,
  type,
  url,
  size,
  record,
  refreshRecord,
  initialData,
  inputs,
  messageList,
  rows,
  hasHeader,
  hasBg,
  containerStyle
} = defineProps({
  accept: {
    type: String,
    default:
      'image/*,.pdf, .txt, .zip, .rar, .7z, .doc, .docx, .csv,.xls, .xlsx, .ppt, .pptx, .mp3, .mp4',
  },
  maxSize: { type: Number, default: 999 },
  type: {
    type: String,
    default: '',
  },
  url: {
    type: String,
    default: '',
  },
  size: {
    type: String,
    default: 'default',
  },
  record: {
    type: Object,
  },
  hasHeader: {
    type: Boolean,
    default: true,
  },
  hasBg: {
    type: Boolean,
    default: true,
  },
  refreshRecord: {
    type: Boolean,
    default: false,
  },
  initialData: {
    type: Object,
    default: {
      // 需要发出去的文本
      message: '',
      // 占位的文本
      placeholder: '',
    },
  },
  inputs: {
    type: Object,
    default: {},
  },
  messageList: {
    type: Array,
    default: [
      // {source: 'ai', answer: '你好，处理', url: '111'},
    ],
  },
  btns: {
    type: String,
    default: 'stop,copy',
    // default: 'stop,copy,file',
  },
  rows: {
    type: Number,
    default: 1,
  },
  containerStyle: {
    type: String,
    default: '',
  },
})
const isUserScroll = ref(false)
const newMessageList = reactive([...messageList])
const inputMessage = ref('')
const aiAvatar = '/path/to/ai-avatar.png'
const userAvatar = '/path/to/user-avatar.png'
const messageContent = ref(null)
const textarea = ref(null)
const files = ref([])
const uploadLoading = ref(false)
const conversation_id = ref('')

watch(inputMessage, () =>
  nextTick(() => {
    const el = textarea.value
    el.style.height = 'auto'
    el.style.height = el.scrollHeight + 'px'
  })
)
const handleKeyDown = (event) => {
    if (event.key === 'Enter') {
      if (event.shiftKey) {
      } else {
        event.preventDefault()
        handleSend()
      }
    }
  };
const scrollToBottom = () => {
  if (isUserScroll.value) return
  messageContent.value.scrollTop = messageContent?.value?.scrollHeight + 60
}

const currentEvent = ref('')
const loading = ref(false)
const currentEvents = ref(new Array(newMessageList.length).fill('message_end'))
const updateCurrentEvents = () => {
  currentEvents.value[newMessageList.length - 1] = currentEvent.value
}

const messageCallback = (currentData, msg) => {
  currentEvent.value = currentData.event
  updateCurrentEvents()
  if (!currentData) {
    console.log('???!!!!2312312')
    loading.value = false
    currentEvent.value = 'message_end'
    return msg && message.info(msg)
  }
  let mes = null,
    mes_id = null
  switch (currentData.event) {
    case 'agent_thought':
      if (currentData.tool) emit('handleThought', currentData)
      break
    case 'message':
    case 'agent_message':
      if (loading.value) loading.value = false
      mes = newMessageList.filter(
        (it) => it.message_id === currentData.message_id
      )
      if (mes.length === 0) {
        newMessageList.push({
          ...currentData,
          time: new Date().getTime(),
          source: 'ai',
        })
      } else {
        mes_id = mes[0].message_id
        for (const key in newMessageList) {
          if (newMessageList[key].message_id === mes_id) newMessageList[key].answer += currentData.answer
           
        }
      }
      break
    case 'message_end':
      if (newMessageList.slice(-1)[0]?.answer.includes('任务保存成功')) emit('handleThought', currentData)
      conversation_id.value = currentData.conversation_id || ''
      loading.value = false
      break
  }
  scrollToBottom()
}
let ctrls = []
const isFlag = () => {
  if (
    ['agent_thought', 'message', 'agent_message'].includes(
      currentEvent.value
    ) ||
    loading.value
  )
    return true
  return false
}
const isLoading = computed(isFlag)
const handleSend = (val = '', currentInputs = {}) => {

  if (isFlag()) return message.info(t('ai', '请等待AI回答'))
  let newVal = inputMessage.value || val || ''
  console.log('ai handleSend',newVal)
  if (newVal.includes(t('ai','我已完成任务'))) files.value = []
  if (!(newVal.trim())) return
  loading.value = true
  currentEvents.value.push('message_end')
  let data = {
    query: newVal,
    conversation_id: conversation_id.value,
    inputs: {...inputs, ...currentInputs},
  }
  newMessageList.push({
    ...data,
    source: 'user',
    answer: newVal,
  })
  ctrls = new Array(newMessageList.length + 1)
    .fill()
    .map(() => new AbortController())
  sendMessageToAgent({
    url,
    data,
    callback: messageCallback,
    signal: ctrls[newMessageList.length - 1].signal,
  })
  inputMessage.value = ''
  isUserScroll.value = false
  nextTick(() => {
    scrollToBottom()
  })
}
const handleStop = () => {
  loading.value = false
  currentEvent.value = ''
  ctrls.map((val) => val.abort())
}
const handleAudio = () => {}


// const renderMarkDown = (text) => {
//   const md = new MarkdownIt({
//     html: true,
//     linkify: true,
//     typographer: true,
//   })
//   return md.render(text)
// }
const renderMarkDown = (text) => {
  const md = new MarkdownIt({
    html: true,
    linkify: true,
    typographer: true,
  })
  return md.render(text).replace(/<a\s+[^>]*?(?!target=)([^>]*>)/g, function(match, p1) {
    return `<a target="_blank" ${p1}`;
  })
}

const handleCopy = (text) => {
  if (navigator.clipboard) {
    navigator.clipboard
      .writeText(text)
      .then(() => {
        message.success(t('common','复制成功'))
      })
      .catch((err) => {
        message.error(t('common','复制失败，请重试'))
      })
  } else {
    const input = document.createElement('input')
    input.style.position = 'fixed'
    input.style.opacity = '0'
    input.value = text
    document.body.appendChild(input)
    input.select()
    try {
      document.execCommand('copy')
      message.success(t('common','复制成功'))
    } catch (err) {
      message.error(t('common','复制失败，请重试'))
    }
    document.body.removeChild(input)
  }
}
const handleReadFile = async (e) => {
  console.log(e, '?')
  if (e.message) return message.error(e.message)
  uploadLoading.value = true
  let res = await learningTaskSubmitTask(record.id, { file: e.file })
  uploadLoading.value = false
  if (!res) return
  message.success(t('common', '上传成功'))
  files.value.push({
    name: e.file.name,
    id: res?.data || '',
  })
}

const handleFilesDelete = async (item, index) => {
  console.log(item, index, '?')
  let res = await learningTaskDelFile(item.id)
  if (!res) return
  message.success(t('common', '操作成功'))
  files.value.splice(index, 1)
}

const emit = defineEmits()
const initialOperation = () => {
  if (initialData.message) {
    inputMessage.value = initialData.message
    handleSend()
  }
}

watch(
  () => [record],
  () => {
    console.log('????', record)
    conversation_id.value = ''
    if (!refreshRecord) return
    ctrls.map((val) => val.abort())
    newMessageList.splice(0, newMessageList.length)
    inputMessage.value = ''
    loading.value = false
    initialOperation()
    scrollToBottom()
  }
)
watch(
  () => currentEvent.value,
  () => {
    emit('update:currentEvent', currentEvent.value)
  }
)
onMounted(async () => {
  initialOperation()
  isUserScroll.value = false
  let lastScrollTop = 0
  messageContent.value.addEventListener('scroll', (e) => {
    if (e.isTrusted) {
      const currentScrollTop = messageContent.value.scrollTop
      if (currentScrollTop < lastScrollTop) isUserScroll.value = true
      else isUserScroll.value = false
      lastScrollTop = currentScrollTop
    }
  })
  scrollToBottom()
})
onUnmounted(() => {
  ctrls.map((val) => val.abort())
})

defineExpose({
  handleSend
})
</script>

<style lang="scss" scoped>
.minAiContainer {
  height: 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
  border-radius: 10px;
  position: relative;
  background: #ffffff;
  box-shadow: 0px 3px 10px 0px #e3e2ff;
  overflow: hidden;
  * {
    letter-spacing: 1px;
  }

  &.small {
    --base: 1px;
    --font-size: 12px;
    --icon-size: 14px;
    --padding: 8px;
  }

  &.default {
    --base: 2px;
    --font-size: 14px;
    --icon-size: 16px;
    --padding: 12px;
  }

  &.large {
    --base: 3px;
    --font-size: 16px;
    --icon-size: 18px;
    --padding: 16px;
  }
}
.minAiHeader {
  position: relative;
  z-index: 1;
  padding: 20px;
  background: transparent;
  display: flex;
  justify-content: space-between;
  align-items: center;
  .logoBox {
    display: flex;
    align-items: center;
    img {
      width: 28px;
      margin-right: 10px;
    }
    .title {
      font-size: var(--icon-size);
      font-weight: 500;
    }
  }
  .closeBtn {
    cursor: pointer;
    font-size: 20px;

    &:hover {
      opacity: 0.8;
    }
  }
}
.minAiBg {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: auto;
  right: 0;
  img {
    width: 100%;
    max-height: 100%;
  }
}
.messageContent {
  height: 100%;
  position: relative;
  z-index: 2;
  flex: 1;
  padding: 10px 20px 145px 20px;
  border-radius: 10px;
  overflow-y: auto;
  /* background: rgba(241, 242, 246, .5); */
  .messageItem {
    display: flex;
    margin-bottom: 10px;
    .avatarBox {
      align-self: flex-end;
      width: 30px;
      height: 30px;
      margin-right: 10px;
      img {
        width: 100%;
        height: 100%;
        border-radius: 50%;
      }
    }
    .textBox {
      border-radius: 6px;
      max-width: 75%;
      display: flex;
      flex-direction: column;
      font-size: var(--font-size);
      .textContent {
        padding: 8px 12px;
        width: 100%;
        word-break: normal;
        display: flex;
        flex-direction: column;
        line-height: 1.3;
      }
      .textBtn {
        padding: 5px 6px;
        cursor: pointer;
        background: #eeeeff;
        border-radius: 6px;
        border: 1px solid #d7d5ff;
        font-weight: 400;
        font-size: 12px;
        color: #8466f5;
        text-align: left;
        font-style: normal;
      }
      ::v-deep img {
        max-width: 200px;
      }
    }
    &.user {
      flex-direction: row-reverse;

      .avatarBox {
        margin-right: 0;
        margin-left: 10px;
      }

      .textContent {
        background: rgba(128, 94, 143, 0.5);
        color: #f5f5f5;
        background: #8f8bff;
        box-shadow: 0px 2px 9px 0px rgba(227, 226, 255, 0.5);
        border-radius: 12px 12px 0px 12px;
      }
    }

    &.ai {
      .textContent {
        background: #ffffff;
        box-shadow: 0px 3px 10px 0px #e3e2ff;
        border-radius: 12px 12px 12px 0px;
        font-weight: 400;
        font-size: var(--font-size);
        color: #333333;
        line-height: 22px;
        text-align: left;
        img {
          max-width: 60px !important;
        }
      }
    }
  }
}

.inputArea {
  z-index: 2;
  position: absolute;
  bottom: 20px;
  /* width: 100%; */
  left: 20px;
  right: 20px;
  margin: auto;
  padding: 10px;
  border-radius: 10px;
  display: flex;
  align-items: flex-end;
  flex-direction: column;
  gap: 8px;
  border-radius: 14px;
  background: #ffffff;
  box-shadow: 0px 6px 12px 0px rgba(0, 0, 0, 0.1);
  border-radius: 14px;
  /* border: 1px solid #D8D8D8; */
  border: 1px solid transparent;
  transition: all 0.3s;
  &:focus-within {
    box-shadow: 0px 6px 12px 0px rgba(118, 83, 212, 0.1);
    border: 1px solid #8f8bff;
  }
  .filesBox {
    position: absolute;
    left: 0;
    bottom: 110%;
    width: 100%;
    display: flex;
    align-items: flex-end;
  }
  .filesList {
    flex: 1;
    display: flex;
    flex-wrap: wrap;
  }

  textarea {
    width: 100%;
    padding: 5px;
    /* flex: 1; */
    line-height: 1.2;
    border: none;
    border-radius: 6px;
    font-family: inherit;
    /* min-height: calc(var(--base) * 24); */
    max-height: calc(var(--base) * 40);
    height: auto;
    resize: none;
    background: transparent;
    height: auto;
    resize: none;
    font-weight: 500;
    font-size: var(--font-size);
    color: #aba6ca;
    line-height: 20px;
    text-align: left;
    outline: none;
    &::placeholder {
      color: #aba6ca; /* 灰色 */
    }
    &:focus {
      outline: none;
      /* border-color: #1976d2;
      box-shadow: 0 0 0 2px rgba(25, 118, 210, 0.1); */
    }
  }

  .actionBtns {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    font-size: 20px;
    .iconBtn {
      width: 32px;
      height: 32px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      background: #f5f5f5;
      transition: all 0.3s;
      font-size: var(--icon-size);
      i {
        color: #666;
      }

      &:hover {
        background: #e0e0e0;
      }

      &.active {
        background: #1976d2;

        i {
          color: white;
        }
      }
    }
    .btn {
      border-radius: 8px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: var(--icon-size);
      width: 36px;
      height: 36px;
      cursor: pointer;
      color: #fff;
      background: #f6f3f6;
      &.primary {
        background: linear-gradient(270deg, #8466f5 0%, #4b66f4 100%);
      }
    }
  }
}
</style>
